package com.yixin.hospital.aop;

import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yixin.hospital.common.BusinessException;
import com.yixin.hospital.common.Constant;
import com.yixin.hospital.entity.domain.OperationLog;
import com.yixin.hospital.entity.domain.User;
import com.yixin.hospital.entity.dto.req.user.LoginReqDTO;
import com.yixin.hospital.entity.dto.req.user.RegisterReqDTO;
import com.yixin.hospital.service.OperationLogService;
import com.yixin.hospital.service.UserService;
import com.yixin.hospital.utils.JwtUtil;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Objects;
import java.util.UUID;

@Aspect
@Component
@Slf4j
public class OperationLogAspect {

    @Resource
    OperationLogService operationLogService;
    @Resource
    UserService userService;

    @Around("@annotation(apiOperation)")
    public Object around(ProceedingJoinPoint joinPoint, ApiOperation apiOperation) {
        try {
            long start = System.currentTimeMillis();
            // 添加MDC
            MDC.put("traceId", UUID.randomUUID().toString().replace("-", ""));

            HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
            String method = request.getMethod();
            // 请求参数
            Object[] args = joinPoint.getArgs();
            String reqParam = "GET".equals(method) ? Arrays.toString(args) :
                    args == null || args.length == 0 ? "" : JSON.toJSONString(args[0]);
            log.info("请求路径:{},请求参数:{}", request.getRequestURI(), reqParam);
            // 接口响应
            Object response = joinPoint.proceed();
            String res = JSON.toJSONString(response);
            log.info("返回参数:{}, cost:{} ms", res, System.currentTimeMillis() - start);

            // GET请求直接返回
            if ("GET".equals(method)) {
                return response;
            }

            // POTS PUT DELETE需要记录日志
            savaLog(apiOperation, request, reqParam, res);
            return response;
        } catch (BusinessException e) {
            log.error("BusinessException ", e);
            throw new BusinessException(e.getMessage());
        } catch (Throwable e) {
            log.error("Exception ", e);
            throw new RuntimeException("服务异常，请联系管理员!");
        }
    }

    // 日志记录不影响主流程
    private void savaLog(ApiOperation apiOperation, HttpServletRequest request, String reqParam, String res) {
        try {
            String userName;
            String value = apiOperation.value();
            if ("注册".equals(value)) {
                RegisterReqDTO registerReqDTO = JSONObject.parseObject(reqParam, RegisterReqDTO.class);
                userName = registerReqDTO.getName();
            } else if ("登录".equals(value)) {
                LoginReqDTO loginReqDTO = JSONObject.parseObject(reqParam, LoginReqDTO.class);
                User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getPhone, loginReqDTO.getPhone()));
                userName = user.getName();
            } else {
                userName = JwtUtil.getName(request.getHeader(Constant.AUTHORIZATION));
            }
            OperationLog operationLog = new OperationLog();
            operationLog.setOperation(value);
            operationLog.setArgs(reqParam);
            operationLog.setRes(res);
            operationLog.setCreatedBy(userName);
            operationLog.setIp(ServletUtil.getClientIP(request));

            operationLogService.save(operationLog);
        } catch (Exception e) {
            log.error("savaLog error: ", e);
        }
    }

}
